#include "utility/log.h"
#include "gl/glew.h"
#include "gl/freeglut.h"
#include "mathematics/mathematics.h"
#include "utility/trackball.h"

static unsigned int window_width = 500;
static unsigned int window_height = 500;
static bool left_button_down = false;
static Matrix3x3 mat3(
    1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 1.0f);
Quaternion rotate;
Trackball tball;

struct Vertex
{
    Vertex(float a = 0.0f, float b = 0.0f, float c = 0.0f)
        : x(a), y(b), z(c)
    {
    }

    float x;
    float y;
    float z;
};


void initGL(int argc, char* argv[]);
void start();

int main(int argc, char* argv[])
{
    initGL(argc, argv);
    start();
    return 0;
}


void onRender(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    
    glLoadIdentity();
    glColor3f(1.0f, 0.0f, 0.0f);

    gluLookAt(
        0, 0, 10,
        0, 0, 0,
        0, 1, 0);

    Vector3 vb[] =
    {
        Vector3(1, 1, 0),
        Vector3(-1, 1, 0),
        Vector3(-1, -1, 0),
        Vector3(1, -1, 0)
    };

    for (int i = 0; i < 4; i++)
    {
        vb[i] = mat3*vb[i];
    }
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vb), vb, GL_STATIC_DRAW);
    glVertexPointer(3, GL_FLOAT, 0, 0);

    glEnableClientState(GL_VERTEX_ARRAY);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glDisableClientState(GL_VERTEX_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glDeleteBuffers(1, &vbo);

    glutSwapBuffers();
}

void onMouseDown(int button, int state, int iPosX, int iPosY)
{
    if (GLUT_LEFT_BUTTON == button)
    {
        if (GLUT_DOWN == state)
        {
            left_button_down = true;
            float x = 2.0 * (float)iPosX / window_width - 1.0f;
            float y = 2.0 * (float)iPosY / window_height - 1.0f;
            tball.startPoint(Vector2(x, y));
        }
        else
        {
            left_button_down = false;
        }
    }
}

void onMouseMove(int iPosX, int iPosY)
{
    Log::info("capture mouse position: x = %d, y = %d\n", iPosX, iPosY);
    float x = 2.0 * (float)iPosX / window_width - 1.0f;
    float y = 2.0 * (float)iPosY / window_height - 1.0f;

    (tball.endPoint(Vector2(x, y)));
    mat3 = tball.GetRotateMatrix();

    glutPostRedisplay();
}


void onKeyDown(unsigned char cKey, int iPosX, int iPosY)
{
    Log::info("capture key down: key = %c, mouse position x = %d, y = %d\n", cKey, iPosX, iPosY);
    switch (cKey)
    {
    case 0x1B:    // esc key code
        Log::info("ESC key pressed\n");
        //glutExit();
        break;
    case 'l':
        glutPostRedisplay();
        break;
    }

}

void onSpecialKeyDown(int key, int iPosX, int iPosY)
{
    Log::info("capture special key down: key = %d, mouse position x = %d, y = %d\n", key, iPosX, iPosY);

    switch (key)
    {
    case 0x001B:

    case GLUT_KEY_UP:
        printf("up\n");
        break;
    case GLUT_KEY_DOWN:
        printf("down\n");
        break;
    case GLUT_KEY_LEFT:
        printf("left\n");
        break;
    case GLUT_KEY_RIGHT:
        printf("right\n");
        break;
    }

    // Refresh the Window
    glutPostRedisplay();
}


void initGL(int argc, char* argv[])
{
    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(window_width, window_height);

    glutCreateWindow("OpenGL test");

    if (GLEW_OK != glewInit())
    {
        Log::error("GLEW init failed.\n");
        throw;
    }

    glShadeModel(GL_SMOOTH);
    glClearDepth(1.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glFrontFace(GL_CCW);        // Counterclockwise polygons face out

    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glColor3f(1.0f, 0.0f, 0.0f);
}


void onSizeChanged(int iWidth, int iHeight)
{
    if (0 == iHeight)
    {
        iHeight = 1;
    }

    window_width = iWidth;
    window_height = iHeight;

    glViewport(0, 0, iWidth, iHeight);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(45.0f, (GLfloat)iWidth/(GLfloat)iHeight, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}


void start()
{
    glutMouseFunc(onMouseDown);
    glutMotionFunc(onMouseMove);
    glutKeyboardFunc(onKeyDown);
    glutSpecialFunc(onSpecialKeyDown);
    glutReshapeFunc(onSizeChanged);
    glutDisplayFunc(onRender);

    glutMainLoop();
}
